package br.caixa2.views;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;

import br.caixa2.dao.AccountDao;
import br.caixa2.dao.ClientDao;
import br.caixa2.entities.AccountImpl;
import br.caixa2.entities.Client;
import br.caixa2.entities.CurrentAccount;
import br.caixa2.entities.SavingAccount;
import br.caixa2.enums.AccountType;
import br.caixa2.enums.TypeOperation;
import br.caixa2.exceptions.AccountNotFound;
import br.caixa2.reports.AccountListReport;
import br.caixa2.reports.ClientListReport;
import br.caixa2.reports.ExtractListReport;
import br.caixa2.views.operations.ExtractVo;
import br.caixa2.views.operations.OperationVo;
import br.caixa2.views.operations.Transfer;

import com.br.generateswingview.core.GVUtils;
import com.br.generateswingview.core.GViewForm;

public class MainView {

	public static void main( String[] args ) {
		final MainView main = new MainView();
		try {
			main.clients = ClientDao.loadAll();
			main.accounts = AccountDao.loadAll();
		} catch ( FileNotFoundException e ) {
			e.printStackTrace();
		}
		final JFrame form = main.getView();
		form.addWindowListener( new WindowListener() {

			@Override
			public void windowOpened( WindowEvent arg0 ) {}

			@Override
			public void windowIconified( WindowEvent arg0 ) {}

			@Override
			public void windowDeiconified( WindowEvent arg0 ) {}

			@Override
			public void windowDeactivated( WindowEvent arg0 ) {}

			@Override
			public void windowClosing( WindowEvent arg0 ) {
				ClientDao.createAll( main.clients );
				AccountDao.createAll( main.accounts );
			}

			@Override
			public void windowClosed( WindowEvent arg0 ) {

			}

			@Override
			public void windowActivated( WindowEvent arg0 ) {

			}
		} );
		form.setVisible( true );
	}

	private JFrame frame;
	private List<AccountImpl> accounts = new ArrayList<AccountImpl>();
	private List<Client> clients = new ArrayList<Client>();

	private final ActionListener createClient = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent arg0 ) {
			GViewForm<Client> view = new GViewForm<Client>( Client.class ) {
				private static final long serialVersionUID = 1L;

				@Override
				public void endAction() {
					Client c = getObject();
					String msn = "";

					if ( !( c.getName() != null && c.getName().replace( " ", "" ).length() >= 5 ) )
						msn += "Preencha o campo nome com no minimo 5 caracteres.\n";
					if ( !( c.getCpf() != null && c.getCpf().replace( " ", "" ).length() >= 13 ) )
						msn += "Preencha o campo Cpf.\n";
					if ( !( c.getPhone() != null && c.getPhone().replace( " ", "" ).length() >= 13 ) )
						msn += "Preencha o campo Telefone.";
					if ( msn.length() > 0 )
						throw new IllegalArgumentException( msn );

					JOptionPane.showMessageDialog( this, "Salvo com sucessso" );
					clients.add( c );
					clearFields();
				}
			};
			view.setVisible( true );
		}
	};

	private final ActionListener createCurrentAccount = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent arg0 ) {
			GViewForm<CurrentAccount> view = new GViewForm<CurrentAccount>( CurrentAccount.class ) {
				private static final long serialVersionUID = 1L;

				@Override
				public void endAction() {
					AccountImpl c = getObject();
					String msn = "";
					if ( c.getClient() == null )
						msn += "Escolha um Cliente\n";
					if ( c.getNumber() > 999999999 || c.getNumber() <= 0 )
						msn += "O numero da conta deve ser menor que 999999999 e maior que zero\n";
					for ( AccountImpl a : accounts )
						if ( a instanceof CurrentAccount && a.getNumber() == c.getNumber() ) {
							msn += "Este número de conta ja existe\n";
							break;
						}
					if ( msn.length() > 0 )
						throw new IllegalArgumentException( msn );
					accounts.add( c );
					JOptionPane.showMessageDialog( this, "Salvo com sucesso!" );
					JOptionPane.showMessageDialog( this, "Senha : " + c.getPassword() );
				}
			};
			setClients( view );
			view.setVisible( true );
		}
	};

	private final ActionListener createSavingAccount = new ActionListener() {
		@Override
		public void actionPerformed( ActionEvent arg0 ) {
			GViewForm<SavingAccount> view = new GViewForm<SavingAccount>( SavingAccount.class ) {
				private static final long serialVersionUID = 1L;

				@Override
				public void endAction() {
					SavingAccount c = getObject();

					String msn = "";
					if ( c.getClient() == null )
						msn += "Escolha um Cliente\n";
					if ( c.getNumber() > 999999999 || c.getNumber() <= 0 )
						msn += "O numero da conta deve ser menor que 999999999 e maior que zero\n";
					for ( AccountImpl a : accounts )
						if ( a instanceof SavingAccount && a.getNumber() == c.getNumber() ) {
							msn += "Este número de conta ja existe\n";
							break;
						}
					if ( msn.length() > 0 )
						throw new IllegalArgumentException( msn );

					accounts.add( c );
					JOptionPane.showMessageDialog( this, "Salvo com sucesso!" );
					JOptionPane.showMessageDialog( this, "Senha : " + c.getPassword() );
					clearFields();
				}
			};
			setClients( view );
			view.setVisible( true );
		}
	};

	private final ActionListener listClientListener = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent arg0 ) {
			try {
				ClientListReport.report( clients );
			} catch ( IOException e ) {
				e.printStackTrace();
			}
		}
	};

	private final ActionListener accountListener = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent arg0 ) {
			try {
				AccountListReport.report( accounts );
			} catch ( IOException e ) {
				e.printStackTrace();
			}
		}
	};

	private final ActionListener drawListener = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent arg0 ) {
			GViewForm<OperationVo> view = new GViewForm<OperationVo>( OperationVo.class ) {

				private static final long serialVersionUID = 1L;

				@Override
				public void endAction() {
					operation( TypeOperation.DRAW, getObject(), this );
				}

			};
			setAccountsType( view );
			view.setVisible( true );
		}
	};

	@SuppressWarnings( "rawtypes" )
	private void operation( TypeOperation type, OperationVo operationVo, GViewForm dialog ) {

		String msn = "";
		if ( operationVo.getNumber() <= 0 )
			msn += "Campo número é obrigatório\n";
		if ( operationVo.getValue() <= 0 )
			msn += "Campo valor é obrigatório, e deve ser maior que zero";
		if ( msn.length() > 0 )
			throw new IllegalArgumentException( msn );

		AccountImpl account = null;
		for ( AccountImpl a : accounts )
			if ( operationVo.getType() == AccountType.CURRENT && a instanceof CurrentAccount ) {
				if ( a.getNumber() == operationVo.getNumber() )
					account = a;
			} else if ( operationVo.getType() == AccountType.SAVING && a instanceof SavingAccount )
				if ( a.getNumber() == operationVo.getNumber() )
					account = a;
		if ( account != null ) {
			if ( !passwordView( account ) )
				throw new IllegalArgumentException( "Senha Incorreta" );
			else {
				if ( type.equals( TypeOperation.DRAW ) )
					account.draw( operationVo.getValue() );
				else
					account.deposit( operationVo.getValue() );
				JOptionPane.showMessageDialog( dialog, "Operação realizada com sucesso" );
				dialog.clearFields();
			}
		} else
			throw new IllegalArgumentException( "Número de conta não encontrado" );
	}

	private final ActionListener depositListener = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent e ) {
			GViewForm<OperationVo> view = new GViewForm<OperationVo>( OperationVo.class ) {

				private static final long serialVersionUID = 6555305889341009209L;

				@Override
				public void endAction() {
					operation( TypeOperation.DEPOSIT, getObject(), this );
				}
			};
			setAccountsType( view );
			view.setVisible( true );

		}
	};
	private final ActionListener transferListener = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent e ) {
			GViewForm<Transfer> view = new GViewForm<Transfer>( Transfer.class ) {

				private static final long serialVersionUID = 1L;

				@Override
				public void endAction() {
					Transfer transfer = getObject();
					String msn = "";
					if ( transfer.getNumberOrigin() <= 0 )
						msn += "O Número da conta origem é obrigátio\n";
					if ( transfer.getValue() <= 0 )
						msn += "O Valor é obrigatório, e deve ser maior que zero\n";
					if ( transfer.getNumberDestiny() <= 0 )
						msn += "O Número da conta destino é obrigátio\n";
					if ( msn.length() > 0 )
						throw new IllegalArgumentException( msn );
					AccountImpl accountDestiny = null;
					AccountImpl accountOrigim = null;
					for ( AccountImpl a : accounts ) {
						if ( transfer.getTypeDestiny() == AccountType.CURRENT && a instanceof CurrentAccount ) {
							if ( a.getNumber() == transfer.getNumberDestiny() )
								accountDestiny = a;
						} else if ( transfer.getTypeDestiny() == AccountType.SAVING && a instanceof SavingAccount )
							if ( a.getNumber() == transfer.getNumberDestiny() )
								accountDestiny = a;

						if ( transfer.getTypeOrigin() == AccountType.CURRENT && a instanceof CurrentAccount ) {
							if ( a.getNumber() == transfer.getNumberOrigin() )
								accountOrigim = a;
						} else if ( transfer.getTypeOrigin() == AccountType.SAVING && a instanceof SavingAccount )
							if ( a.getNumber() == transfer.getNumberOrigin() )
								accountOrigim = a;
					}
					if ( accountOrigim == null )
						throw new AccountNotFound( "Conta origem não econtrada" );
					else if ( accountDestiny == null )
						throw new AccountNotFound( "Conta destino não econtrada" );
					if ( !passwordView( accountOrigim ) )
						throw new IllegalArgumentException( "Senha Incorreta" );

					accountOrigim.transfer( accountDestiny, transfer.getValue() );
					JOptionPane.showMessageDialog( this, "Operação realizada com sucesso" );
					clearFields();
				}

			};
			setAccountsTypeTransfer( view );
			view.setVisible( true );
		}
	};
	private final ActionListener extractListener = new ActionListener() {

		@Override
		public void actionPerformed( ActionEvent e ) {
			GViewForm<ExtractVo> view = new GViewForm<ExtractVo>( ExtractVo.class ) {

				private static final long serialVersionUID = 1L;

				@Override
				public void endAction() {
					ExtractVo extractVo = getObject();
					String msn = "";
					if ( extractVo.getNumber() <= 0 )
						msn += "O Número da conta é obrigátio\n";
					if ( msn.length() > 0 )
						throw new IllegalArgumentException( msn );
					AccountImpl account = null;
					for ( AccountImpl a : accounts )
						if ( extractVo.getType() == AccountType.CURRENT && a instanceof CurrentAccount ) {
							if ( a.getNumber() == extractVo.getNumber() )
								account = a;
						} else if ( extractVo.getType() == AccountType.SAVING && a instanceof SavingAccount )
							if ( a.getNumber() == extractVo.getNumber() )
								account = a;

					if ( account == null )
						throw new AccountNotFound( "Conta origem não econtrada" );

					if ( !passwordView( account ) )
						throw new IllegalArgumentException( "Senha Incorreta" );

					try {
						ExtractListReport.report( account );
					} catch ( IOException e ) {
						e.printStackTrace();
					}
					clearFields();
				}

			};

			setAccountsType( view );
			view.setVisible( true );
		}
	};

	private boolean passwordView( AccountImpl account ) {
		String[] str = account.getPassword().split( "-" );

		boolean pass = true;
		for ( String string : str )
			try {
				ConfirmatePassword confirmatePassword = new ConfirmatePassword( string );
				confirmatePassword.setVisible( true );
				pass &= confirmatePassword.getSelected().contains( string );
			} catch ( Exception e ) {
				e.printStackTrace();
			}
		return pass;
	}

	@SuppressWarnings( "unchecked" )
	private void setAccountsType( JDialog frame ) {
		JComboBox<AccountType> combo = (JComboBox<AccountType>) GVUtils.getElementByName( frame, "type" );
		for ( AccountType type : AccountType.values() )
			combo.addItem( type );
	}

	@SuppressWarnings( "unchecked" )
	private void setAccountsTypeTransfer( JDialog frame ) {
		JComboBox<AccountType> combo = (JComboBox<AccountType>) GVUtils.getElementByName( frame, "typeOrigin" );
		for ( AccountType type : AccountType.values() )
			combo.addItem( type );
		combo = (JComboBox<AccountType>) GVUtils.getElementByName( frame, "typeDestiny" );
		for ( AccountType type : AccountType.values() )
			combo.addItem( type );
	}

	@SuppressWarnings( "unchecked" )
	private void setClients( JDialog frame ) {
		JComboBox<Client> combo = (JComboBox<Client>) GVUtils.getElementByName( frame, "client" );
		for ( Client client : clients )
			combo.addItem( client );
	}

	public JFrame getView() {
		frame = new JFrame( "HomeBank Caixa II" );
		Toolkit kit = Toolkit.getDefaultToolkit();
		Dimension screenSize = kit.getScreenSize();
		frame.setSize( screenSize.width / 2, screenSize.height / 2 );
		frame.setLocationRelativeTo( null );
		frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
		frame.setJMenuBar( createMenu() );
		frame.setResizable( false );
		return frame;
	}

	private JMenuBar createMenu() {
		JMenuBar menuBar = new JMenuBar();
		JMenu menu = new JMenu( "Castrar" );
		JMenuItem item1 = new JMenuItem( "Cliente" );
		item1.addActionListener( createClient );
		menu.add( item1 );
		JMenuItem item2 = new JMenuItem( "Conta Corrente" );
		menu.add( item2 );
		item2.addActionListener( createCurrentAccount );
		menuBar.add( menu );
		JMenuItem item3 = new JMenuItem( "Conta Poupança" );
		menu.add( item3 );
		item3.addActionListener( createSavingAccount );
		menuBar.add( menu );

		menu = new JMenu( "Listar" );
		item1 = new JMenuItem( "Clientes" );
		item1.addActionListener( listClientListener );
		menu.add( item1 );
		item2 = new JMenuItem( "Contas" );
		item2.addActionListener( accountListener );
		menu.add( item2 );
		menuBar.add( menu );

		menu = new JMenu( "Operações em conta" );
		item1 = new JMenuItem( "Sacar" );
		item1.addActionListener( drawListener );
		menu.add( item1 );
		item2 = new JMenuItem( "Despositar" );
		item2.addActionListener( depositListener );
		menu.add( item2 );
		item2 = new JMenuItem( "Transferir" );
		item2.addActionListener( transferListener );
		menu.add( item2 );
		item2 = new JMenuItem( "Extrato" );
		item2.addActionListener( extractListener );
		menu.add( item2 );
		menuBar.add( menu );
		return menuBar;
	}

	public List<AccountImpl> getAccounts() {
		return accounts;
	}

	public List<Client> getClients() {
		return clients;
	}

}
